package com.crealth.crmserver;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.log4j.Logger;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.WriteRequest;
import org.apache.mina.filter.firewall.Subnet;

import com.crealth.db.WhitelistDb;
import com.crealth.model.WhiteListIPModel;


/** 
 * Whitelist filter (based on Mina's Blacklist filter). 
 */  
public class WhitelistFilter extends IoFilterAdapter {
    private static List<Subnet> whitelist = 
    		new CopyOnWriteArrayList<Subnet>();
    private static Logger logger = Logger.getLogger("white_list");
    
    public WhitelistFilter() {
    	List<WhiteListIPModel> whitels = WhitelistDb.selectAllWhiteList();
    	for (WhiteListIPModel whiteIP : whitels) {
    		String strIP = whiteIP.getWhiteIP();
    		if (strIP == null || strIP.length() < 5) {
    			continue;
    		}
    		try {    			
				InetAddress addr = InetAddress.getByName(strIP);
				allow(addr);
			} catch (UnknownHostException e) {
			}
    	}
    }
  
    public static void setWhitelist(InetAddress[] addresses) {  
        if (addresses == null) {  
            throw new NullPointerException("addresses");  
        }  
        whitelist.clear();  
        for (InetAddress addr : addresses) {  
            allow(addr);  
        }  
    }  
  
    public static void setSubnetWhitelist(Subnet[] subnets) {  
        if (subnets == null) {  
            throw new NullPointerException("Subnets must not be null");  
        }  
        whitelist.clear();  
        for (Subnet subnet : subnets) {
            allow(subnet);  
        }  
    }  
  
    public void setWhitelist(Iterable<InetAddress> addresses) {  
        if (addresses == null) {  
            throw new NullPointerException("addresses");  
        }  
  
        whitelist.clear();  
  
        for (InetAddress address : addresses) {  
            allow(address);  
        }  
    }  
  
    public void setSubnetWhitelist(Iterable<Subnet> subnets) {  
        if (subnets == null) {  
            throw new NullPointerException("Subnets must not be null");  
        }  
        whitelist.clear();  
        for (Subnet subnet : subnets) {  
            allow(subnet);  
        }  
    }  
  
    public static void allow(InetAddress address) {  
        if (address == null) {  
            throw new NullPointerException("Adress to block can not be null");  
        }  
  
        allow(new Subnet(address, 32));  
    }  
  
    public static void allow(Subnet subnet) {
        if (subnet == null) {  
            throw new NullPointerException("Subnet can not be null");  
        }  
  
        whitelist.add(subnet);  
    }  
  
    public void disallow(InetAddress address) {  
        if (address == null) {  
            throw new NullPointerException("Adress to unblock can not be null");  
        }  
  
        disallow(new Subnet(address, 32));  
    }  
  
    public void disallow(Subnet subnet) {  
        if (subnet == null) {  
            throw new NullPointerException("Subnet can not be null");  
        }  
        whitelist.remove(subnet);  
    }  
  
    @Override  
    public void sessionCreated(NextFilter nextFilter, IoSession session) {  
        if (isAllowed(session)) {  
            nextFilter.sessionCreated(session);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    @Override  
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {  
        if (isAllowed(session)) {  
            nextFilter.sessionOpened(session);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    @Override  
    public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {  
        if (isAllowed(session)) {  
            nextFilter.sessionClosed(session);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    @Override  
    public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {  
        if (isAllowed(session)) {  
            nextFilter.sessionIdle(session, status);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    @Override  
    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) {  
        if (isAllowed(session)) {  
            nextFilter.messageReceived(session, message);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    @Override  
    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {  
        if (isAllowed(session)) {  
            nextFilter.messageSent(session, writeRequest);  
        } else {  
            blockSession(session);  
        }  
    }  
  
    private void blockSession(IoSession session) {
    	String remoteIP = CRMServerHandler.getClientIP(session);
    	logger.warn("Remote address: "+remoteIP+" is not allowed; closing.");  
        session.close(true);
    }  
  
    private boolean isAllowed(IoSession session) {  
        SocketAddress remoteAddress = session.getRemoteAddress();  
        if (remoteAddress instanceof InetSocketAddress) {  
            InetAddress address = ((InetSocketAddress) remoteAddress).getAddress();
            // check all subnets   
            for (Subnet subnet : whitelist) {  
                if (subnet.inSubnet(address)) {  
                    return true;  
                }  
            }  
        }  
        return false;  
    }  
}
